1 /*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.common.base;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21 import static java.util.concurrent.TimeUnit.DAYS;
22 import static java.util.concurrent.TimeUnit.HOURS;
23 import static java.util.concurrent.TimeUnit.MICROSECONDS;
24 import static java.util.concurrent.TimeUnit.MILLISECONDS;
25 import static java.util.concurrent.TimeUnit.MINUTES;
26 import static java.util.concurrent.TimeUnit.NANOSECONDS;
27 import static java.util.concurrent.TimeUnit.SECONDS;
28
29 import com.google.common.annotations.Beta;
30 import com.google.common.annotations.GwtCompatible;
31
32 import java.util.concurrent.TimeUnit;
33
34 /**
35 * An object that measures elapsed time in nanoseconds. It is useful to measure
36 * elapsed time using this class instead of direct calls to {@link
37 * System#nanoTime} for a few reasons:
38 *
39 * <ul>
40 * <li>An alternate time source can be substituted, for testing or performance
41 * reasons.
42 * <li>As documented by {@code nanoTime}, the value returned has no absolute
43 * meaning, and can only be interpreted as relative to another timestamp
44 * returned by {@code nanoTime} at a different time. {@code Stopwatch} is a
45 * more effective abstraction because it exposes only these relative values,
46 * not the absolute ones.
47 * </ul>
48 *
49 * <p>Basic usage:
50 * <pre>
51 * Stopwatch stopwatch = Stopwatch.{@link #createStarted createStarted}();
52 * doSomething();
53 * stopwatch.{@link #stop stop}(); // optional
54 *
55 * long millis = stopwatch.elapsed(MILLISECONDS);
56 *
57 * log.info("time: " + stopwatch); // formatted string like "12.3 ms"</pre>
58 *
59 * <p>Stopwatch methods are not idempotent; it is an error to start or stop a
60 * stopwatch that is already in the desired state.
61 *
62 * <p>When testing code that uses this class, use
63 * {@link #createUnstarted(Ticker)} or {@link #createStarted(Ticker)} to
64 * supply a fake or mock ticker.
65 * <!-- TODO(kevinb): restore the "such as" --> This allows you to
66 * simulate any valid behavior of the stopwatch.
67 *
68 * <p><b>Note:</b> This class is not thread-safe.
69 *
70 * @author Kevin Bourrillion
71 * @since 10.0
72 */
73 @Beta
74 @GwtCompatible(emulated = true)
75 public final class Stopwatch {
76 private final Ticker ticker;
77 private boolean isRunning;
78 private long elapsedNanos;
79 private long startTick;
80
81 /**
82 * Creates (but does not start) a new stopwatch using {@link System#nanoTime}
83 * as its time source.
84 *
85 * @since 15.0
86 */
87 public static Stopwatch createUnstarted() {
88 return new Stopwatch();
89 }
90
91 /**
92 * Creates (but does not start) a new stopwatch, using the specified time
93 * source.
94 *
95 * @since 15.0
96 */
97 public static Stopwatch createUnstarted(Ticker ticker) {
98 return new Stopwatch(ticker);
99 }
100
101 /**
102 * Creates (and starts) a new stopwatch using {@link System#nanoTime}
103 * as its time source.
104 *
105 * @since 15.0
106 */
107 public static Stopwatch createStarted() {
108 return new Stopwatch().start();
109 }
110
111 /**
112 * Creates (and starts) a new stopwatch, using the specified time
113 * source.
114 *
115 * @since 15.0
116 */
117 public static Stopwatch createStarted(Ticker ticker) {
118 return new Stopwatch(ticker).start();
119 }
120
121 /**
122 * Creates (but does not start) a new stopwatch using {@link System#nanoTime}
123 * as its time source.
124 *
125 * @deprecated Use {@link Stopwatch#createUnstarted()} instead.
126 */
127 @Deprecated
128 Stopwatch() {
129 this(Ticker.systemTicker());
130 }
131
132 /**
133 * Creates (but does not start) a new stopwatch, using the specified time
134 * source.
135 *
136 * @deprecated Use {@link Stopwatch#createUnstarted(Ticker)} instead.
137 */
138 @Deprecated
139 Stopwatch(Ticker ticker) {
140 this.ticker = checkNotNull(ticker, "ticker");
141 }
142
143 /**
144 * Returns {@code true} if {@link #start()} has been called on this stopwatch,
145 * and {@link #stop()} has not been called since the last call to {@code
146 * start()}.
147 */
148 public boolean isRunning() {
149 return isRunning;
150 }
151
152 /**
153 * Starts the stopwatch.
154 *
155 * @return this {@code Stopwatch} instance
156 * @throws IllegalStateException if the stopwatch is already running.
157 */
158 public Stopwatch start() {
159 checkState(!isRunning, "This stopwatch is already running.");
160 isRunning = true;
161 startTick = ticker.read();
162 return this;
163 }
164
165 /**
166 * Stops the stopwatch. Future reads will return the fixed duration that had
167 * elapsed up to this point.
168 *
169 * @return this {@code Stopwatch} instance
170 * @throws IllegalStateException if the stopwatch is already stopped.
171 */
172 public Stopwatch stop() {
173 long tick = ticker.read();
174 checkState(isRunning, "This stopwatch is already stopped.");
175 isRunning = false;
176 elapsedNanos += tick - startTick;
177 return this;
178 }
179
180 /**
181 * Sets the elapsed time for this stopwatch to zero,
182 * and places it in a stopped state.
183 *
184 * @return this {@code Stopwatch} instance
185 */
186 public Stopwatch reset() {
187 elapsedNanos = 0;
188 isRunning = false;
189 return this;
190 }
191
192 private long elapsedNanos() {
193 return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos;
194 }
195
196 /**
197 * Returns the current elapsed time shown on this stopwatch, expressed
198 * in the desired time unit, with any fraction rounded down.
199 *
200 * <p>Note that the overhead of measurement can be more than a microsecond, so
201 * it is generally not useful to specify {@link TimeUnit#NANOSECONDS}
202 * precision here.
203 *
204 * @since 14.0 (since 10.0 as {@code elapsedTime()})
205 */
206 public long elapsed(TimeUnit desiredUnit) {
207 return desiredUnit.convert(elapsedNanos(), NANOSECONDS);
208 }
209
210 private static TimeUnit chooseUnit(long nanos) {
211 if (DAYS.convert(nanos, NANOSECONDS) > 0) {
212 return DAYS;
213 }
214 if (HOURS.convert(nanos, NANOSECONDS) > 0) {
215 return HOURS;
216 }
217 if (MINUTES.convert(nanos, NANOSECONDS) > 0) {
218 return MINUTES;
219 }
220 if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
221 return SECONDS;
222 }
223 if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
224 return MILLISECONDS;
225 }
226 if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
227 return MICROSECONDS;
228 }
229 return NANOSECONDS;
230 }
231
232 private static String abbreviate(TimeUnit unit) {
233 switch (unit) {
234 case NANOSECONDS:
235 return "ns";
236 case MICROSECONDS:
237 return "\u03bcs"; // μs
238 case MILLISECONDS:
239 return "ms";
240 case SECONDS:
241 return "s";
242 case MINUTES:
243 return "min";
244 case HOURS:
245 return "h";
246 case DAYS:
247 return "d";
248 default:
249 throw new AssertionError();
250 }
251 }
252 }